package com.realxxs.demo.realxxsredisbitmap.controller;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/***
 *@Author xiekang
 *@Date 2020/12/17 16:12
 *
 **/
@Controller
@RequestMapping("/redis")
public class RedisBitMapController {

     private String userId="155852412069";

    @Autowired
    RedisTemplate<String, String> redisTemplate;


    /**
     * 获取 当月签到总次数
     * @param
     * @return
     */
    @GetMapping("/getKey")
    public String getRedis() {
        String s1 = buildSignKey(userId, LocalDate.now());
        Object s = redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(s1.getBytes()));
        return String.valueOf(s);
    }

    /**
     * 模拟签到
     * @param signDay yyyy-MM-dd
     * @return
     */
    @GetMapping("/sign")
    public String setBitFild(@RequestParam String signDay) {
        LocalDate localDate = formatDay(signDay);
        redisTemplate.opsForValue().setBit(buildSignKey(userId, localDate), localDate.getDayOfMonth(), true);
        return localDate.getDayOfMonth() + "日签到成功";
    }

    /**
     * 模拟取消签到  用来测试数据用
     * @param signDay yyyy-MM-dd
     * @return
     */
    @GetMapping("/unsign")
    public String unsetBitFild(@RequestParam String signDay) {
        LocalDate localDate = formatDay(signDay);
        redisTemplate.opsForValue().setBit(buildSignKey(userId, localDate), localDate.getDayOfMonth(), false);
        return localDate.getDayOfMonth() + "日取消签到成功";
    }

    /**
     * 获取一个当前月份里面  截止到到传入的日期的连续签到的天数
     * @param signDay
     * @return
     */
    @GetMapping("/getSign")
    public long getBitFild(@RequestParam String signDay) {
        LocalDate localDate = formatDay(signDay);
        long continueSignCountDay = getContinueSignCountDay(userId, localDate, 1);
        System.out.println(continueSignCountDay);
        return continueSignCountDay;
    }

    /**
     * 检测某一天是否签到
     * @param signDay
     * @return
     */
    @GetMapping("/checkSign")
    public Boolean checkSign(@RequestParam String signDay) {
        LocalDate localDate = formatDay(signDay);
        Boolean bit = redisTemplate.opsForValue().getBit(buildSignKey(userId, localDate), localDate.getDayOfMonth());
        return bit;
    }


    public LocalDate formatDay(String signDay) {
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return LocalDate.parse(signDay, format);
    }

    /**
     * 获取一个月签到的详情
     * @return
     */
    @GetMapping("/getsignDetail")
    @ResponseBody
    public String setBitFild1() {
        Map<String, Boolean> signInfo = getSignInfo(userId, LocalDate.now());
        System.out.println(signInfo);
        return JSON.toJSONString(signInfo);
    }


    /**
     * 使用bitfield来判断用户连续签到多少天
     *
     * @param signKey
     * @param limit
     * @param offset
     * @return
     */
    public List<Long> bitfiled(String signKey, int limit, int offset) {
        List<Long> execute = (List<Long>) redisTemplate.execute((RedisCallback<List<Long>>) con -> con.bitField(
                signKey.getBytes(), BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset)));
        return execute;
    }


    private static String buildSignKey(String uid, LocalDate date) {
        return String.format("u:sign:%s:%s", uid, formatDate(date, "yyyyMM"));
    }

    private static String formatDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取该用户的当前自然月的签到总数
     *
     * @param userId
     * @param localDate
     * @return
     */
    public long getContinueSignCountDay(String userId, LocalDate localDate, int offset) {
        long signDayCount = 0;
        List<Long> list = bitfiled(buildSignKey(userId, localDate), localDate.getDayOfMonth(), offset);
        System.out.println(list.get(0));
        if (!CollectionUtils.isEmpty(list) && list.size() > 0) {
            long result = list.get(0) == null ? 0 : list.get(0);
            for (int i = 0; i < localDate.getDayOfMonth(); i++) {
                if (result >> 1 << 1 == result) {
                    if (i > 0) {
                        break;
                    }
                } else {
                    signDayCount += 1;
                }
                result >>= 1;
            }
        }
        return signDayCount;
    }


    /**
     * 月签详情 查看哪天签到 哪天未签到
     *
     * @param userId
     * @param date
     * @return
     */

    public Map<String, Boolean> getSignInfo(String userId, LocalDate date) {
        Map<String, Boolean> signMap = new LinkedHashMap<>(date.getDayOfMonth());
        List<Long> list = bitfiled(buildSignKey(userId, date), date.lengthOfMonth(), 1);
        System.out.println(list.get(0));
        if (!CollectionUtils.isEmpty(list) && list.size() > 0) {
            // 由低位到高位，为0表示未签，为1表示已签
            long result = list.get(0) == null ? 0 : list.get(0);
            for (int i = date.lengthOfMonth(); i > 0; i--) {
                LocalDate d = date.withDayOfMonth(i);
                signMap.put(formatDate(d, "yyyy-MM-dd"), result >> 1 << 1 != result);
                result >>= 1;
            }
        }
        return signMap;
    }





}
